#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

typedef  int u32;// 自定义类型
struct Teacher
{
    char name[100];
    int age;
};
typedef struct Teacher Teacher;

typedef struct _Teacher2
{
    char name[100];
    int age;
}Teacher2;

void test()
{
    int a[10];
    printf("&a : %d,a:%d \n",&a,a);
    printf("&a : %d,a+1:%d \n",&a+1,a+1);
    //    &a : 6356584,a:6356584
    //    &a : 6356624,a+1:6356588
    // 同样的数字 +1 不一样.
    // 问题的本质是数据类型不一样,a是什么类型,&a是什么类型
    // 压死初学者的三座大山:1.数组类型, 2.数组指针, 3.数组类型和数组指针的关系.
    // 数据类型分为两种:1.简单的数据类型,2.复杂的数据类型.碰见复杂的数据类型不能够用简单的数据类型去分析它.
}
void test1()
{
    // 数据类本质是什么?-->思考数据类型和内存有关系吗,什么关系?为什么大牛们,会引用这些数据类型.
    int a;// 分配4个字节内存
    // 意思是我定义一个变量a;告诉c++编译器给我分配4个字节内存.写给c++编译看的.
    // java程序员面向业务流的.c++不光面向业务流,还要面向操作系统.

    // 学不好内存,哪来的指针,指针是为内存服务的.没有内存哪来的指针.
    int a1[10];// 告诉c++分配40个字节内存
    //----------------------------------------

    // 数据类型本质 -->无论是c#,java都一样
    // 数据类型可理解为创建变量的模具,是固定内存大小的别名. 各种类型各分配固定内存
    // 数据类型的作用:编译器预算对象(变量)分配的内存空间大小.
    // 数据类型只是模具,编译器没有分配空间,只有根据类型创建变量,才会分配内存空间.
    // char 1byte; int 4byte;
    // c++大牛给咱们提供了 sizeof();测出空间大小
    printf("sizeof(int) : %d,sizeof(int *) : %d\n", sizeof(int),sizeof(int *));
    // 在windows下
    // 32位 sizeof(int) : 4,sizeof(int *) : 4
    // 64位 sizeof(int) : 4,sizeof(int *) : 8
    printf("sizeof(u32) : %d,sizeof(u32 *) : %d\n", sizeof(u32),sizeof(u32 *));
    //    类型可以有别名,typedef
    //    sizeof是操作符。不是函数，sizeof测量的是实体大小,在编译期间就已经确定.
}
void test2()
{
    int a = 10;
    int b[10];
    printf("int a : %d,int  * : %d,int b : %d\n",sizeof(a),sizeof(int *),sizeof(b));
    printf("int b[0] : %d,*b : %d\n",sizeof(b[0]),sizeof(*b));
    // 32位下int a : 4,int  * : 4,int b : 40,int b[0] : 4,*b : 4
    // 64位下int a : 4,int  * : 8,int b : 40,int b[0] : 4,*b : 4
}
add(int a,int b)
{
    return a+b;
}

void test3()
{
    // 1. void 字面是无类型,void* 则为无类型指针,可以指向任何类型的数据
    // 2. 用法1: 数据类型的封装
    // int InitHardEnv(void **handle);
    //    典型的如内存操作函数memcpy和memset函数原型为
    //    void * memcpy(void *dest,const void * src,size_t len);
    //    void * memset(void *buffer,int c,size_t num);
    // 用法2:void修饰的函数返回值和参数,仅表示无
    //    如果函数没有返回值,那么久将其声明为void型
    //    如果函数没有返回值,那么声明为void类型.不然就会被编译器作为整形值处理.
    printf("add 2 + 3 = %d\n",add(2,3));// add 2 + 3 = 5
    // C++语言有很严格的类型安全检查，不允许上述情况（指函数不加类型声明）发生,所以对于任何函数都必须一个不漏的指定类型,如果函数没有返回值,则声明为void.
    // 如果int func(void),调用func(1)失败,如果是int func(),则func(2)调用成功.在c++中.这样的代码会出错.

    // 3. 如果没有参数,将其声明为void
    //    int function(void){retrun 1;}

    // 4.void 指针的意义 : c语言规定只有相同类型的指针才可以相互赋值
    // void* 指针作为左值用于*接收任意类型的指针
    // void*作为右值赋值给其它指针时,需要强制类型转换
    float *p1;
    int *p2;
    // 可能在gcc下会编译通过
    //    p1 = p2;// error C2440: “=”: 无法从“int *”转换为“float *”; warning assignment from incompatible pointer type
    p1 = (float *)p2;
    // int *p = NULL; char* p2 = (char *)malloc(sizeof(char)*20);
    // 5. 不存在void类型的变量:c语言没有定义void究竟是多大的内存的别名
    //    void a1;// error: variable or field 'a1' declared void
    printf("sizeof(void):%d\n",sizeof(void));
    //gcc下 sizeof(void):0
    // vs 下 error C2070: “void”: 非法的 sizeof 操作数
    // 6.如果函数的参数可以是任意类型指针，那么应声明其参数为void *
}
void test4()
{
    // 变量本质:
    // 1.概念:既可以读也可以写的内存对象,叫变量,一旦初始化后不能修改的对象叫常量
    // int x;
    // 变量本质: 变量是一段连续的实实在在的内存空间的别名,内存空间的标号.
    //    数据类型是固定内存大小的别名,而变量则是一段连续内存空间的别名,这段空间的值,则是变量的值,这个变量存放的位置则是内存标号,也就是变量名称.
    // 修改内存
    int a; // 告诉编译器给我分配4字节内存
    a =10; // 直接修改
    printf("a = %d,&a = %d\n",a,&a);
    *(&a) = 20;
    printf("a = %d,&a = %d\n",a,&a);
//    a = 10,&a = -2056259696
//    a = 20,&a = -2056259696
    // 变量可读可写,通过往变量编号地址读写数据,不是向变量本身读写数据,变量名称是指的内存编号0xffffff,变量的值代码内存编号所存储的内存空间.
}

#if 0
void testasm()
{
    int a = 6;
    int b = 5;
    // __asm__ 表示后面的代码为内嵌汇编,"asm" 是"__asm__"的别名.
    // __volatile__ 表示编译器不要优化代码,后面代码指令保持原样,"volatile"是它的别名
    // 括号内为汇编指令
    // movl %0,%1"是指令模板,%0和%1代表指令的操作数,称为占位符,最多十个.


    asm volatile("movl %0,%1" : "=r" (a) : "r" (b));
    // 等价于 mov eax,b
    // 表示将b读入寄存器，并且输出给a所以现在a和b都等于5.

    printf("a = %d,b =%d",a,b);
    a = 2;
    b = 3;
    // 内嵌汇编
    __asm__
    (
    "addl %%ebx, %%eax"
    : "=a" (a)
            : "b" (b), "a" (a)
            );
    printf("a = %d,b =%d",a,b);
}

#endif
int main()
{
    //        test();
    //    test1();
    //    test2();
//    test3();
     test4();
    //    testasm();
    printf("Hello World!\n");
    return 0;
}
